package com.oswift.utils.file;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.oswift.utils.common.StreamUtil;

/**
 *
 * 使用zip格式，对文件进行压缩、解压缩操作
 *
 * @author zhuou
 * @version C03 Oct 27, 2012
 * @since OSwift GPM V1.0
 */
public final class FileZipUtil
{
    /**
     * FileZipUtil的实例
     */
    private static FileZipUtil zipUtil = new FileZipUtil();

    // 获取日志对象
    private static Logger log = LoggerFactory.getLogger(FileZipUtil.class);

    /**
     * 构造函数
     */
    private FileZipUtil()
    {
    }

    /**
     * 获取FileZipUtil的实例
     *
     * @return FileZipUtil的实例
     */
    public static FileZipUtil getInstance()
    {
        return zipUtil;
    }

    /**
     * 用于对指定源文件路径下的所有文件压缩到指定的文件中
     *
     * @param dest
     *            String 需要压缩成的文件路径
     * @param src
     *            String 压缩文件源路径,可以是文件或文件夹
     * @return boolean 如果压缩成功返回true，否则表示失败
     */
    public boolean file2Zip(String dest, String src)
    {
        // 压缩成功标志
        boolean success = true;
        ZipOutputStream out = null;
        FileOutputStream fos = null;

        try
        {
            fos = new FileOutputStream(new File(dest));

            // 创建压缩输出流
            out = new ZipOutputStream(fos);

            // 开始压缩
            compress(new File(src), out, "");
        }
        catch (IOException ex1)
        {
            log.error("Fail to zip src file:" + src + " to dest file:" + dest,
                    ex1);
            success = false;
        }
        finally
        {
            StreamUtil.getInstance().close(out, fos);
        }

        return success;
    }

    /**
     * 文件压缩
     *
     * @param src
     *            File
     * @param out
     *            ZipOutputStream
     * @param base
     *            String
     * @throws IOException
     * @throws IOException
     * @throws IOException
     */
    private void compress(File src, ZipOutputStream out, String base)
        throws IOException
    {
        if (src.isDirectory())
        {
            // 如果是目录
            // 列举出目录中所有的内容
            File[] files = src.listFiles();

            // 放入一级目录
            try
            {
                out.putNextEntry(new ZipEntry(base + "/"));
            }
            catch (IOException e)
            {
                throw e;
            }

            base = null == base || base.length() == 0 ? "" : base + "/";
            for (File file : files)
            {
                compress(file, out, base + file.getName());
            }
        }
        else
        {
            FileInputStream in = null;
            try
            {
                // 如果是文件
                if ("".equals(base))
                {
                    out.putNextEntry(new ZipEntry(base + "/"));
                    base = src.getName();
                }
                out.putNextEntry(new ZipEntry(base));

                in = new FileInputStream(src);
                final int initSize = 4096;
                byte[] data = new byte[initSize];
                int b;
                while ((b = in.read(data)) != -1)
                {
                    out.write(data, 0, b);
                }
            }
            catch (FileNotFoundException e)
            {
                log.error("File [" + src + "] not found.");
                throw e;
            }
            catch (IOException e)
            {
                throw e;
            }
            finally
            {
                StreamUtil.getInstance().close(in);
            }
        }
    }

    /**
     * 该方法用于解压zip文件
     *
     * @param zipPath
     *            要解压的zip文件路径
     * @param outputDirectory
     *            解压文件存放的文件夹
     * @return 成功与否
     */
    public boolean decompressZip(String zipPath, String outputDirectory)
    {
        FileInputStream zip = null;
        ZipInputStream zis = null;

        // 创建解压文件目录
        File directory = new File(outputDirectory);
        if (checkDir(directory))
        {
            try
            {
                zip = new FileInputStream(zipPath);
                zis = new ZipInputStream(zip);
                ZipEntry ze = zis.getNextEntry();
                while (ze != null)
                {
                    if (!ze.isDirectory())
                    {
                        String entyPath = ze.getName();
                        File file = new File(outputDirectory + File.separator
                                + entyPath);

                        // 创建文件夹
                        String filePath = entyPath.replace('\\', '/');

                        if (filePath.indexOf("/") != -1)
                        {
                            String dirPath = filePath.substring(0, filePath
                                    .lastIndexOf("/"));
                            File dir = new File(outputDirectory
                                    + File.separator + dirPath);
                            if (!checkDir(dir))
                            {
                                return false;
                            }
                        }

                        doWriteFile(zis, file);

                    }
                    else
                    {
                        String entyPath = ze.getName();
                        File dir = new File(outputDirectory + File.separator
                                + entyPath);

                        if (!checkDir(dir))
                        {
                            if (directory.exists())
                            {
                                FileUtil.deleteFile(directory);
                            }

                            return false;
                        }
                    }

                    ze = zis.getNextEntry();
                }

                return true;
            }
            catch (Exception e2)
            {
                log.error(String.format("Compress zip file : %s failed!",
                        zipPath), e2);

                if (directory.exists())
                {
                    FileUtil.deleteFile(directory);
                }

                return false;
            }
            finally
            {
                StreamUtil.getInstance().close(zis);
            }

        }
        else
        {
            return false;
        }

    }

    /**
     * 检查文件目录有效性
     *
     * @param dir
     *            文件目录
     * @return 是否有效
     */
    private boolean checkDir(File dir)
    {
        return dir.exists() || dir.mkdirs();
    }

    /**
     * 解压过程中的读写文件
     *
     * @param in
     *            压缩文件输入流
     * @param out
     *            压缩后的输出流
     * @throws IOException
     */
    private void doWriteFile(InputStream zis, File out) throws IOException
    {
        byte[] entyContent = null;
        FileOutputStream baos = null;

        if (zis != null && out != null)
        {
            try
            {
                baos = new FileOutputStream(out);
                entyContent = new byte[1024 * 20];
                int len = zis.read(entyContent);
                while (len > 0)
                {
                    baos.write(entyContent, 0, len);
                    len = zis.read(entyContent);
                }
            }
            finally
            {
                StreamUtil.getInstance().close(baos);

                // 释放内存片
                entyContent = null;
            }
        }
    }
}
